// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/src"
)

// Func对应于Go程序中的单个函数
// （反之亦然：每个函数用一个*Func表示）。
// None
// IR中有多个表示Func的节点。
// None
// ONAME节点（Func.Nname）用于对它的普通引用。
// ODCLFUNC节点（Func本身）用于其声明代码。
// OCLOSURE节点（Func.OCLOSURE）用于对
// 函数文本。
// None
// 导入的函数将有一个指向Func的ONAME节点
// 空荡荡的身体。
// 声明的函数或方法具有ODCLFUNC（函数本身）和ONAME。
// 函数文字直接由OCLOSURE表示，但它也
// 具有一个ODCLFUNC（和一个匹配的ONAME），表示已编译的
// 闭包的底层形式，用于访问捕获的变量
// 使用在寄存器中传递的特殊数据结构。
// None
// 方法声明表示为函数，f.Sym除外
// 将是限定的方法名称（例如，“T.m”），并且
// f、 Func.Shortname是裸方法名（例如，“m”）。
// None
// 方法表达式（T.M）表示为一个节点，
// 其中n.Left和n.Right分别指向类型和方法。
// 源代码中对方法表达式的每次不同提及
// 构造一个新节点。
// None
// 方法值（t.M）由ODOTMETH/ODOTINTER表示
// 当它被直接调用，或者被OCALLPART调用时。
// 这些表达式类似于方法表达式，除了ODOTMETH/ODOTINTER，
// 方法名称存储在Sym中，而不是右侧。
// 每个OCALLPART最终都作为一个新的
// 函数，有点像闭包，有自己的ODCLFUNC。
// OCALLPART使用n.Func记录到的链接
// 已生成ODCLFUNC，但没有
// 从Func返回到OCALLPART的指针。
type Func struct {
	miniNode
	Body Nodes
	Iota int64

	Nname    *Name        // 小名节点
	OClosure *ClosureExpr // 闭合节点

	Shortname *types.Sym

	// 函数的额外输入代码。例如，分配和初始化
	// 用于转义参数的内存。
	Enter Nodes
	Exit  Nodes

	// 此函数/闭包的所有参数/局部变量的ONAME节点不可用
	// 包括closurevars，直到在漫游期间转换闭包。
	// 名字必须是PPARAMs，PPARAMOUTs，然后是PAUTOs，
	// 按照与函数签名对应的顺序使用PPARAMs和PPARAMOUTs。
	// 但是，由于没有实际声明匿名或空白PPARAM，
	// 它们在Dcl中被省略。
	// 匿名和空白pparamout分别声明为~rNN和~bNN名称。
	Dcl []*Name

	// ClosureVars列出了在
	// 函数文本，但在封闭的
	// 作用此切片中的变量是闭包函数的
	// 在其函数中使用的变量的自身副本
	// 身体它们还将分别设置IsClosureVar和
	// 如果它们被值捕获，则设置Byval。
	ClosureVars []*Name

	// 需要编译的封闭函数。
	// 步行时居住。
	Closures []*Func

	// Parents记录一个文件中每个作用域的父作用域
	// 作用根作用域（0）没有父作用域，因此
	// 作用域的父项存储在父项[i-1]中。
	Parents []ScopeID

	// 标记记录范围边界的更改。
	Marks []Mark

	FieldTrack map[*obj.LSym]struct{}
	DebugInfo  interface{}
	LSym       *obj.LSym // 此函数的本机ABI（Func.ABI）中的链接器对象

	Inl *Inline

	// Closgen跟踪在中生成了多少个闭包
	// 这个功能。closurename用于创建唯一的
	// 函数名。
	Closgen int32

	Label int32 // 此函数中自动生成的最大标签

	Endlineno src.XPos
	WBPos     src.XPos // 第一写入屏障的位置；见SetWBPos

	Pragma PragmaFlag // go:xxx函数注释

	flags bitset16

	// ABI是函数的“定义”ABI。这就是
	// 此函数生成的代码预期由调用。
	// None
	// 对于大多数函数，这将是obj.ABIInternal。可能是
	// 程序集或ABI包装器中定义的函数的不同ABI。
	// None
	// 这包括在导出数据中，并跨包跟踪。
	ABI obj.ABI
	// ABIRefs是引用此函数的ABI集。
	// 对于除此函数的定义ABI之外的ABI
	// 编译器生成ABI包装函数。这只是跟踪
	// 在一个包裹里。
	ABIRefs obj.ABISet

	NumDefers  int32 // 函数中的延迟调用数
	NumReturns int32 // 函数中显式返回的数目

	// nwbrCalls记录此函数调用的函数的LSyms
	// go:nowritebarrierrec分析功能。只填写
	// 如果没有，请检查！=无
	NWBRCalls *[]SymAndPos
}

func NewFunc(pos src.XPos) *Func {
	f := new(Func)
	f.pos = pos
	f.op = ODCLFUNC
	f.Iota = -1
	// 大多数函数都是内部函数。进口商或symabis
	// pass可能会覆盖此选项。
	f.ABI = obj.ABIInternal
	return f
}

func (f *Func) isStmt() {}

func (n *Func) copy() Node                         { panic(n.no("copy")) }
func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
func (n *Func) editChildren(edit func(Node) Node)  { editNodes(n.Body, edit) }

func (f *Func) Type() *types.Type                { return f.Nname.Type() }
func (f *Func) Sym() *types.Sym                  { return f.Nname.Sym() }
func (f *Func) Linksym() *obj.LSym               { return f.Nname.Linksym() }
func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) }

// 内联保存用于可内联的函数体的字段。
type Inline struct {
	Cost int32 // 内联此函数的启发式代价

	// 用于内联的funct.Dcl和funct.Body的副本。副本是
	// 需要，因为函数的dcl/主体可能会被以后的编译器更改
	// 转变。当函数从
	// 另一个包是进口的。
	Dcl  []*Name
	Body []Node
}

// 标记表示范围边界。
type Mark struct {
	// Pos是标记范围的标记的位置
	// 改变
	Pos src.XPos

	// Scope标识Pos右侧最内层的范围。
	Scope ScopeID
}

// ScopeID表示函数中的词法作用域。
type ScopeID int32

const (
	funcDupok         = 1 << iota // 重复定义ok
	funcWrapper                   // 对用户隐藏帧（在回溯中删除，不算作recover（）的帧）
	funcABIWrapper                // 是ABI包装器（也可设置flagWrapper）
	funcNeedctxt                  // 函数使用上下文寄存器（具有闭包变量）
	funcReflectMethod             // 函数调用reflect.Type.Method或MethodByName
	// 如果函数内部有闭包，则为true；如果是简单函数或
	// 全局变量初始化中的闭包
	funcIsHiddenClosure
	funcHasDefer                 // 包含延迟语句
	funcNilCheckDisabled         // 编译此函数时禁用零检查
	funcInlinabilityChecked      // inliner已确定函数是否可内联
	funcExportInline             // 在导出数据中包含内联正文
	funcInstrumentBody           // 在SSA构建期间添加race/msan仪器
	funcOpenCodedDeferDisallowed // 无法执行开放编码延迟
	funcClosureCalled            // 关闭只会立即调用
)

type SymAndPos struct {
	Sym *obj.LSym // 被叫方的LSym
	Pos src.XPos  // 电话线
}

func (f *Func) Dupok() bool                    { return f.flags&funcDupok != 0 }
func (f *Func) Wrapper() bool                  { return f.flags&funcWrapper != 0 }
func (f *Func) ABIWrapper() bool               { return f.flags&funcABIWrapper != 0 }
func (f *Func) Needctxt() bool                 { return f.flags&funcNeedctxt != 0 }
func (f *Func) ReflectMethod() bool            { return f.flags&funcReflectMethod != 0 }
func (f *Func) IsHiddenClosure() bool          { return f.flags&funcIsHiddenClosure != 0 }
func (f *Func) HasDefer() bool                 { return f.flags&funcHasDefer != 0 }
func (f *Func) NilCheckDisabled() bool         { return f.flags&funcNilCheckDisabled != 0 }
func (f *Func) InlinabilityChecked() bool      { return f.flags&funcInlinabilityChecked != 0 }
func (f *Func) ExportInline() bool             { return f.flags&funcExportInline != 0 }
func (f *Func) InstrumentBody() bool           { return f.flags&funcInstrumentBody != 0 }
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
func (f *Func) ClosureCalled() bool            { return f.flags&funcClosureCalled != 0 }

func (f *Func) SetDupok(b bool)                    { f.flags.set(funcDupok, b) }
func (f *Func) SetWrapper(b bool)                  { f.flags.set(funcWrapper, b) }
func (f *Func) SetABIWrapper(b bool)               { f.flags.set(funcABIWrapper, b) }
func (f *Func) SetNeedctxt(b bool)                 { f.flags.set(funcNeedctxt, b) }
func (f *Func) SetReflectMethod(b bool)            { f.flags.set(funcReflectMethod, b) }
func (f *Func) SetIsHiddenClosure(b bool)          { f.flags.set(funcIsHiddenClosure, b) }
func (f *Func) SetHasDefer(b bool)                 { f.flags.set(funcHasDefer, b) }
func (f *Func) SetNilCheckDisabled(b bool)         { f.flags.set(funcNilCheckDisabled, b) }
func (f *Func) SetInlinabilityChecked(b bool)      { f.flags.set(funcInlinabilityChecked, b) }
func (f *Func) SetExportInline(b bool)             { f.flags.set(funcExportInline, b) }
func (f *Func) SetInstrumentBody(b bool)           { f.flags.set(funcInstrumentBody, b) }
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
func (f *Func) SetClosureCalled(b bool)            { f.flags.set(funcClosureCalled, b) }

func (f *Func) SetWBPos(pos src.XPos) {
	if base.Debug.WB != 0 {
		base.WarnfAt(pos, "write barrier")
	}
	if !f.WBPos.IsKnown() {
		f.WBPos = pos
	}
}

// FuncName返回函数n的名称（不带包）。
func FuncName(f *Func) string {
	if f == nil || f.Nname == nil {
		return "<nil>"
	}
	return f.Sym().Name
}

// PkgFuncName返回n引用的函数的名称，并在包的前面加上前缀。
// 这与编译器的内部约定不同，在编译器内部约定中，局部函数缺少包
// 因为它的最终消费者是一个看IDE的人；包裹只是空的
// 如果编译包实际上是空字符串。
func PkgFuncName(f *Func) string {
	if f == nil || f.Nname == nil {
		return "<nil>"
	}
	s := f.Sym()
	pkg := s.Pkg

	p := base.Ctxt.Pkgpath
	if pkg != nil && pkg.Path != "" {
		p = pkg.Path
	}
	if p == "" {
		return s.Name
	}
	return p + "." + s.Name
}

var CurFunc *Func

func FuncSymName(s *types.Sym) string {
	return s.Name + "·f"
}

// MarkFunc将节点标记为函数。
func MarkFunc(n *Name) {
	if n.Op() != ONAME || n.Class != Pxxx {
		base.Fatalf("expected ONAME/Pxxx node, got %v", n)
	}

	n.Class = PFUNC
	n.Sym().SetFunc(true)
}

// ClosureDebugRuntimeCheck应用调试标志的样板检查
// 编译运行时
func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
	if base.Debug.Closure > 0 {
		if clo.Esc() == EscHeap {
			base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
		} else {
			base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
		}
	}
	if base.Flag.CompilingRuntime && clo.Esc() == EscHeap {
		base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime")
	}
}

// IsTrivalClosure报告Close是否具有
// 捕获的变量的空列表。
func IsTrivialClosure(clo *ClosureExpr) bool {
	return len(clo.Func.ClosureVars) == 0
}
